package com.uxsino.simo.query;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.uxsino.commons.utils.config.ConfigProp;
import com.uxsino.commons.utils.config.PropElement;
import com.uxsino.commons.utils.debug.SourceInfo;
import com.uxsino.simo.indicator.CompoundIndicator;
import com.uxsino.simo.indicator.Indicator;
import com.uxsino.simo.indicator.ListIndicator;
import com.uxsino.simo.utils.ConfigLoadingContext;

public abstract class QueryCombine<ResultIndicatorType extends Indicator, ValueType>
    implements IQueryCombine<ResultIndicatorType, ValueType> {

    private ResultIndicatorType resultIndicator;

    private List<QueryTemplate> subQuerires = new ArrayList<>();

    @ConfigProp(name = "priority")
    @JsonProperty("priority")
    @JSONField(name = "priority")
    private int priority = DEFAULT_PRIORITY;

    public QueryCombine(ResultIndicatorType resultIndicator) {
        this.resultIndicator = resultIndicator;
    }

    @Override
    public ResultIndicatorType getResultIndicator() {
        return resultIndicator;
    }

    @Override
    public List<QueryTemplate> getQueries() {
        ArrayList<QueryTemplate> l = new ArrayList<>();
        l.addAll(subQuerires);
        return l;
    }

    @Override
    public void addQuery(QueryTemplate query) {
        subQuerires.add(query);
    }

    public static IQueryCombine<?, ?> create(String name, Indicator resultIndicator) {
        switch (name) {
            case "union":
            return new UnionQueryCombine((ListIndicator) resultIndicator);
            case "join":
            return new JoinQueryCombine((ListIndicator) resultIndicator);
            case "merge":
            return new CompoundMergeQueryCombine((CompoundIndicator) resultIndicator);
            case "aggregate":
            return new AggrQueryCombine((CompoundIndicator) resultIndicator);
            case "script":
            return new ScriptQueryCombine((ListIndicator) resultIndicator);
            case "listUnion":
            return new ListUnionQueryCombine((ListIndicator) resultIndicator);
        }
                throw new IllegalArgumentException("unknown combine type:" + name);
        }

    @Override
    public void foreach(Consumer<QueryTemplate> action) {
        for (QueryTemplate qt : subQuerires) {
            action.accept(qt);
        }
    }

    @Override
    public int count() {
        return subQuerires.size();
    }

    @Override
    public String getQueryType() {
        return QUERY_TYPE_COMBINE;
    }

    @Override
    public void forEachIndicator(Consumer<Indicator> action) {
        action.accept(getResultIndicator());
    }

    @Override
    public boolean retractsIndicator(Indicator indicator, String fieldName) {

        if (indicator != getResultIndicator()){
            return false;
        }

        if (fieldName == null) {
            return true;
        }
        for (QueryTemplate qt : subQuerires) {
            if (qt.retractsIndicator(indicator, fieldName)){
                return true;
            }
        }

        return false;
    }

    @Override
    public void loadProp(PropElement element, ConfigLoadingContext lctxt) {
        try {
            lctxt.getPropLoader().loadProperties(this, element);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public int getPriority() {
        return priority;
    }

    private SourceInfo sourceInfo = new SourceInfo();

    @Override
    public SourceInfo getSourceInfo() {
        return sourceInfo;
    }

    @Override
    @JsonIgnore
    public void setSourceInfo(String sourceName, String sourceLocation) {
        sourceInfo.source = sourceName;
        sourceInfo.location = sourceLocation;
    }
}
